namespace ImapComponent.Net.Mail.Utils
{
    using System;
    using System.Collections;
    using System.IO;
    using System.Text;
    using System.Web;

    /// <summary>
    /// Provides all logging facilities for any application.
    /// </summary>
    public class Logger
    {
        #region Constants

        private const int queueSize = 20;

        #endregion // Constants

        #region Fields

        private readonly static Queue lastLogEntries = new Queue(queueSize);
        private readonly static ArrayList logEntries = new ArrayList();
        private static string logFile = string.Empty;
        private static int loggingLevel = 0;
        private static bool isDisabled = false;
        private static bool logInMemory = false;
        private static bool useTraceContext = false;
        private static bool useTraceConsole = false;

        #endregion // Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the Logger class.
        /// </summary>
        private Logger()
        {
        }

        #endregion // Constructors

        #region Properties

        /// <summary>
        /// Gets the last log entries.
        /// </summary>
        public static Queue LastLogEntries
        {
            get
            {
                return lastLogEntries;
            }
        }

        /// <summary>
        /// Gets the log entries that are stored in the memory.
        /// </summary>
        public static ArrayList LogEntries
        {
            get
            {
                return logEntries;
            }
        }

        /// <summary>
        /// Gets or sets the full path to the text file to append to when logging.
        /// </summary>
        public static string LogFile
        {
            get
            {
                return logFile;
            }

            set
            {
                logFile = value;
            }
        }

        /// <summary>
        /// Gets or sets the logging level.
        /// </summary>
        public static int LoggingLevel
        {
            get
            {
                return loggingLevel;
            }

            set
            {
                loggingLevel = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the logging functions are disabled or not.
        /// </summary>
        public static bool IsDisabled
        {
            get
            {
                return isDisabled;
            }

            set
            {
                isDisabled = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether log entries are kept in memory or not.
        /// </summary>
        public static bool LogInMemory
        {
            get
            {
                return logInMemory;
            }

            set
            {
                logInMemory = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the logger needs to append to the Trace Context.
        /// </summary>
        public static bool UseTraceContext
        {
            get
            {
                return useTraceContext;
            }

            set
            {
                useTraceContext = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the logger needs to append to the Console.
        /// </summary>
        public static bool UseTraceConsole
        {
            get
            {
                return useTraceConsole;
            }

            set
            {
                useTraceConsole = value;
            }
        }

        /// <summary>
        /// Gets the number of log entries.
        /// </summary>
        public static int Count
        {
            get
            {
                return logEntries.Count;
            }
        }

        #endregion // Properties

        #region Methods

        /// <summary>
        /// Add a log entry using the logging level.
        /// </summary>
        /// <param name="entry">The entry to add.</param>
        public static void AddEntry(string entry)
        {
            AddEntry(entry, 0);
        }

        /// <summary>
        /// Add a log entry in all available logging objects.
        /// </summary>
        /// <param name="entry">The entry to add.</param>
        /// <param name="level">The log entry level.</param>
        public static void AddEntry(string entry, int level)
        {
            if (!isDisabled && level >= loggingLevel)
            {
                string nowString = String.Format("{0:dd/MM/yyyy HH:mm:ss}", DateTime.Now);
                StringBuilder logString = new StringBuilder();
                logString.Append(nowString);
                logString.Append(" ");
                logString.Append(entry);

                if (!string.IsNullOrEmpty(logFile))
                {
                    AddEntryToFile(logString.ToString());
                }

                if (useTraceContext)
                {
                    AddEntryToTrace(logString.ToString());
                }

                if (useTraceConsole)
                {
                    AddEntryToConsole(logString.ToString());
                }

                if (logInMemory)
                {
                    logEntries.Add(logString.ToString());
                }

                lastLogEntries.Enqueue(logString.ToString());

                OnEntryAdded(new LoggerEventArgs(entry));
            }
        }

        /// <summary>
        /// Append the logging to the text file.
        /// </summary>
        /// <param name="entry">The entry to add.</param>
        protected static void AddEntryToFile(string entry)
        {
            if (!isDisabled)
            {
                StreamWriter fileWriter = new StreamWriter(logFile, true, UTF8Encoding.Default);
                fileWriter.WriteLine(entry);
                fileWriter.Close();
            }
        }

        /// <summary>
        /// Append the logging to the console.
        /// </summary>
        /// <param name="entry">The entry to add.</param>
        protected static void AddEntryToConsole(string entry)
        {
            if (!isDisabled)
            {
                Console.WriteLine("Mail: {0}", entry);
            }
        }

        /// <summary>
        /// Append the logging to the trace context.
        /// </summary>
        /// <param name="entry">The entry to add.</param>
        protected static void AddEntryToTrace(string entry)
        {
            if (!isDisabled)
            {
                if (HttpContext.Current != null)
                {
                    HttpContext.Current.Trace.Write("Mail", entry);
                }
            }
        }

        /// <summary>
        /// Gets an ArrayList containing the specified number of last entries.
        /// </summary>
        /// <param name="lines">The maximum lines to retrieve.</param>
        /// <returns>An ArrayList containing the maximum log entries.</returns>
        public static ArrayList LastEntries(int lines)
        {
            ArrayList entries = new ArrayList();

            if (logInMemory)
            {
                int count = Count;

                for (int index = (lines <= count) ? (count - lines) : 0; index <= count; index++)
                {
                    entries.Add(logEntries[index]);
                }
            }

            return entries;
        }

        /// <summary>
        /// Gets the last entry of the log.
        /// </summary>
        /// <returns>A string containing the last entry.</returns>
        public static string LastEntry()
        {
            if (logInMemory && Count > 0)
            {
                return logEntries[Count - 1].ToString();
            }

            return string.Empty;
        }

        #endregion // Methods

        #region Events and Event Triggers

        /// <summary>
        /// The entry added delegate handler.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Logger event arguments.</param>
        public delegate void EntryAddedHandler(object sender, LoggerEventArgs e);

        /// <summary>
        /// The EntryAdded event handler.
        /// </summary>
        public static event EntryAddedHandler EntryAdded;

        /// <summary>
        /// On EntryAdded event.
        /// </summary>
        /// <param name="e">Logger event arguments.</param>
        protected static void OnEntryAdded(LoggerEventArgs e)
        {
            if (EntryAdded != null)
            {
                EntryAdded(null, e);
            }
        }

        #endregion // Events and Event Triggers
    }
}
